﻿Imports System
Imports System.Collections.Generic
Imports System.Workflow.ComponentModel

Namespace System.Workflow.Activities
    <Serializable> _
    Friend Class StateMachineExecutionState
        ' Methods
        Friend Sub New(ByVal instanceId As Guid)
            Me._subscriptionManager = New StateMachineSubscriptionManager(Me, instanceId)
        End Sub

        Friend Sub CalculateStateTransition(ByVal currentState As StateActivity, ByVal targetStateName As String)
            If (currentState Is Nothing) Then
                Throw New ArgumentNullException("currentState")
            End If
            If String.IsNullOrEmpty(targetStateName) Then
                Throw New ArgumentNullException("targetStateName")
            End If
            Do While ((Not currentState Is Nothing) AndAlso (currentState.QualifiedName.Equals(targetStateName) OrElse Not StateMachineHelpers.ContainsState(currentState, targetStateName)))
                Dim item As New CloseStateAction(currentState.QualifiedName)
                Me.Actions.Enqueue(item)
                currentState = [TryCast](currentState.Parent,StateActivity)
            Loop
            If (currentState Is Nothing) Then
                Throw New InvalidOperationException(SR.GetUnableToTransitionToState(targetStateName))
            End If
            Do While Not currentState.QualifiedName.Equals(targetStateName)
                Dim activity As Activity
                For Each activity In currentState.EnabledActivities
                    Dim state As StateActivity = [TryCast](activity,StateActivity)
                    If ((Not state Is Nothing) AndAlso StateMachineHelpers.ContainsState(state, targetStateName)) Then
                        Dim action2 As New ExecuteChildStateAction(currentState.QualifiedName, state.QualifiedName)
                        Me.Actions.Enqueue(action2)
                        currentState = state
                        Exit For
                    End If
                Next
            Loop
            If Not StateMachineHelpers.IsLeafState(currentState) Then
                Throw New InvalidOperationException(SR.GetInvalidStateTransitionPath)
            End If
        End Sub

        Friend Function DequeueAction() As StateMachineAction
            Dim action As StateMachineAction = Me.Actions.Dequeue
            If (Me.Actions.Count = 0) Then
                Me._queueLocked = False
            End If
            Return action
        End Function

        Friend Sub EnqueueAction(ByVal action As StateMachineAction)
            Me.Actions.Enqueue(action)
        End Sub

        Friend Shared Function [Get](ByVal state As StateActivity) As StateMachineExecutionState
            Return DirectCast(state.GetValue(StateActivity.StateMachineExecutionStateProperty), StateMachineExecutionState)
        End Function

        Friend Sub LockQueue()
            Me._queueLocked = True
        End Sub

        Friend Sub ProcessActions(ByVal context As ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            If Not Me.SchedulerBusy Then
                Dim state As StateActivity = DirectCast(context.Activity, StateActivity)
                If (Me.Actions.Count = 0) Then
                    Me.SubscriptionManager.ProcessQueue(context)
                Else
                    Dim action As StateMachineAction = Me.Actions.Peek
                    Do While action.StateName.Equals(state.QualifiedName)
                        action = Me.DequeueAction
                        action.Execute(context)
                        If Me.SchedulerBusy Then
                            Return
                        End If
                        If (Me.Actions.Count = 0) Then
                            Exit Do
                        End If
                        action = Me.Actions.Peek
                    Loop
                    If (Me.Actions.Count > 0) Then
                        Dim activity3 As StateActivity = StateMachineHelpers.FindDynamicStateByName(StateMachineHelpers.GetRootState(state), action.StateName)
                        If (activity3 Is Nothing) Then
                            Throw New InvalidOperationException(SR.GetInvalidStateMachineAction(action.StateName))
                        End If
                        activity3.RaiseProcessActionEvent(context)
                    Else
                        Me.SubscriptionManager.ProcessQueue(context)
                    End If
                End If
            End If
        End Sub

        Friend Sub ProcessTransitionRequest(ByVal context As ActivityExecutionContext)
            If Not String.IsNullOrEmpty(Me.NextStateName) Then
                Dim currentState As StateActivity = StateMachineHelpers.GetCurrentState(context)
                Me.CalculateStateTransition(currentState, Me.NextStateName)
                Me.LockQueue
                Me.NextStateName = Nothing
            End If
        End Sub


        ' Properties
        Private ReadOnly Property Actions As Queue([Of] StateMachineAction)
            Get
                If (Me._actions Is Nothing) Then
                    Me._actions = New Queue([Of] StateMachineAction)
                End If
                Return Me._actions
            End Get
        End Property

        Friend Property Completed As Boolean
            Get
                Return Me._completed
            End Get
            Set(ByVal value As Boolean)
                Me._completed = value
            End Set
        End Property

        Friend Property CurrentStateName As String
            Get
                Return Me._currentStateName
            End Get
            Set(ByVal value As String)
                Me._currentStateName = value
            End Set
        End Property

        Friend ReadOnly Property HasEnqueuedActions As Boolean
            Get
                Return (Me.Actions.Count > 0)
            End Get
        End Property

        Friend Property NextStateName As String
            Get
                Return Me._nextStateName
            End Get
            Set(ByVal value As String)
                Me._nextStateName = value
            End Set
        End Property

        Friend Property PreviousStateName As String
            Get
                Return Me._previousStateName
            End Get
            Set(ByVal value As String)
                Me._previousStateName = value
            End Set
        End Property

        Friend Property SchedulerBusy As Boolean
            Get
                Return Me._schedulerBusy
            End Get
            Set(ByVal value As Boolean)
                Me._schedulerBusy = value
            End Set
        End Property

        Friend ReadOnly Property SubscriptionManager As StateMachineSubscriptionManager
            Get
                Return Me._subscriptionManager
            End Get
        End Property


        ' Fields
        Private _actions As Queue([Of] StateMachineAction)
        Private _completed As Boolean
        Private _currentStateName As String
        Private _nextStateName As String
        Private _previousStateName As String
        Private _queueLocked As Boolean
        Private _schedulerBusy As Boolean
        Private _subscriptionManager As StateMachineSubscriptionManager
        Friend Const StateMachineExecutionStateKey As String = "StateMachineExecutionState"
    End Class
End Namespace

